package bma.common.langutil.ai;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import bma.common.langutil.ai.callback.AICallback;
import bma.common.langutil.ai.callback.AICallbackCore;
import bma.common.langutil.ai.common.AIResultHandler;
import bma.common.langutil.ai.future.AIFSimple;
import bma.common.langutil.ai.future.AIFuture;
import bma.common.langutil.ai.future.AIFutureCallbackable;
import bma.common.langutil.ai.stack.AICallbackToStack;
import bma.common.langutil.ai.stack.AIStack;
import bma.common.langutil.ai.stack.AIStackReentrant;
import bma.common.langutil.ai.stack.AIStackRunnable;
import bma.common.langutil.ai.stack.AIStackWrapForFuture;
import bma.common.langutil.ai.vm.AIContext;
import bma.common.langutil.concurrent.ListenableFuture;
import bma.common.langutil.core.Function;

/**
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 * 
 */
public class AIUtil {

	public static <TYPE> ListenableFuture<TYPE> simpleFuture(TYPE v, Throwable e) {
		return new AIFSimple<TYPE>(v, e);
	}

	public static <RESP_TYPE> AIStack<RESP_TYPE> futureStack(
			AIStack<RESP_TYPE> s) {
		if (s instanceof AIFuture) {
			return s;
		}
		return new AIStackWrapForFuture<RESP_TYPE>(s);
	}

	public static <RESP_TYPE> AIStack<RESP_TYPE> futureCallbackStack(
			AIStack<RESP_TYPE> s) {
		if (s instanceof AIFutureCallbackable) {
			return s;
		}
		return new AIStackWrapForFuture<RESP_TYPE>(s);
	}

	@SuppressWarnings("unchecked")
	public static <RESP_TYPE> AIFuture<RESP_TYPE> stackFuture(
			AIStack<RESP_TYPE> s) {
		if (s instanceof AIFuture) {
			return (AIFuture) s;
		}
		return new AIStackWrapForFuture<RESP_TYPE>(s);
	}

	@SuppressWarnings("unchecked")
	public static <RESP_TYPE> AIStack<RESP_TYPE> stack(AIFuture<RESP_TYPE> s) {
		return (AIStack<RESP_TYPE>) s;
	}

	public static Throwable exception(Throwable t) {
		while (t != null) {
			if (t instanceof ExecutionException) {
				t = ((ExecutionException) t).getCause();
			} else {
				return t;
			}
		}
		return t;
	}

	public static <RESP_TYPE> AICallback<RESP_TYPE> callback(
			AIStack<RESP_TYPE> stack) {
		return new AICallbackToStack<RESP_TYPE>(stack);
	}

	public static <RESP_TYPE> AICallback<RESP_TYPE> callback(
			final AIStack<?> stack, final AIResultHandler<RESP_TYPE> rh) {
		return new AICallback<RESP_TYPE>() {
			@Override
			public void onFailure(Throwable t) {
				stack.failure(t);
			}

			@Override
			public void onSuccess(RESP_TYPE result) {
				rh.success(result);
			};
		};
	}

	public static <RESP_TYPE, CALL_TYPE> AICallback<CALL_TYPE> callback(
			final AIStack<RESP_TYPE> stack,
			final Function<CALL_TYPE, RESP_TYPE> fun) {
		return new AICallback<CALL_TYPE>() {
			@Override
			public void onFailure(Throwable t) {
				stack.failure(t);
			}

			@Override
			public void onSuccess(CALL_TYPE result) {
				stack.success(fun.apply(result));
			};
		};
	}

	public static <TYPE> TYPE get(AIFutureCallbackable<TYPE> stack,
			int timeout, TimeUnit unit) throws InterruptedException,
			TimeoutException, ExecutionException {
		AICallbackCore<TYPE> cb = new AICallbackCore<TYPE>();
		stack.callback(cb);
		return cb.get(timeout, unit);
	}

	public static <TYPE> TYPE get(AIFutureCallbackable<TYPE> stack)
			throws InterruptedException, ExecutionException {
		AICallbackCore<TYPE> cb = new AICallbackCore<TYPE>();
		stack.callback(cb);
		return cb.get();
	}

	public static <RESP_TYPE> boolean safeSuccess(AIStack<RESP_TYPE> stack,
			RESP_TYPE result) {
		try {
			return stack.success(result);
		} catch (Throwable t) {
			return safeFailure(stack, t);
		}
	}

	public static <RESP_TYPE> boolean safeFailure(AIStack<RESP_TYPE> stack,
			Throwable t) {
		try {
			return stack.failure(t);
		} catch (Throwable t2) {
			return true;
		}
	}

	public static <RESP_TYPE> void executeFailure(AIContext ctx,
			final AIStack<RESP_TYPE> stack, final Throwable t) {
		ctx.execute(new Runnable() {
			@Override
			public void run() {
				safeFailure(stack, t);

			}
		});
	}

	public static <RESP_TYPE> void executeSuccess(AIContext ctx,
			final AIStack<RESP_TYPE> stack, final RESP_TYPE result) {
		ctx.execute(new Runnable() {
			@Override
			public void run() {
				safeSuccess(stack, result);

			}
		});
	}

	public static <RESP_TYPE> AIStack<RESP_TYPE> reentrant(
			AIStack<RESP_TYPE> stack, AIContext ctx) {
		return new AIStackReentrant<RESP_TYPE>(stack, ctx);
	}

	public static <RESP_TYPE> void callAt(AIContext ctx,
			final AIStack<RESP_TYPE> stack, final AIStackRunnable<RESP_TYPE> fun) {
		fun.bindStack(stack);
		ctx.execute(fun);
	}
}
